import { isFunction, isPlainObject } from './utils'
import { PageLifecycle, createLifecycleMethods } from './lifecycle'
import { wrapFuns } from './shared'
import { createContext, IContext } from './context'
import { setCurrentInstance, PageInstance } from './instance'
import { reactive } from './reactive'

type Setup<Porps extends Object> = (props: Porps, context: IContext) => Record<string, any>
type PageSetup = Setup<Record<string, any>>
type PageOptions = WechatMiniprogram.Page.Options<Record<string, any>, Record<string, any>>

/** 
 * 页面入口方法
 * @param setup {PageSetup} setup方法
 */
export function VPage(
    options: PageSetup | ({ setup?: PageSetup } & PageOptions)
) {
    let setupFn: PageSetup // setup函数
    let miniOptions: Record<string, any> = {} // 小程序Page原本的参数
    if (isFunction(options)) {
        // options为函数，则options为setup函数
        setupFn = options
    } else {
        if (options.setup === undefined) {
            // 如果options没有setup函数，则直接执行小程序Page方法
            return Page(options)
        }
        let { setup: setupFunc, ...otherOptions } = options
        setupFn = setupFunc
        miniOptions = otherOptions
    }

    // onLoad生命周期
    miniOptions[PageLifecycle.LOAD] = wrapFuns(function (
        this: PageInstance,
        options: Record<string, string>
    ) {
        setCurrentInstance(this); // 保存Page实例，供给setup中的生命周期使用
        let context = createContext.call(this) // VPage创建实例，并绑定Page实例
        let SetupTask: Record<string, string> = setupFn.call(this, reactive(options), context) // 执行setup方法
        if (!isPlainObject(SetupTask)) console.error('setup返回格式错误')
        context.setData(SetupTask) // 将setup返回数据插入Page实例中
        setCurrentInstance(null); // 清空储存的Page实例
    }, createLifecycleMethods(PageLifecycle.LOAD, miniOptions))

    // 其他生命周期
    for (let key in PageLifecycle) {
        switch (PageLifecycle[key]) {
            case PageLifecycle.LOAD:
                break;
            case PageLifecycle.ADD_TO_FAVORITES:
            case PageLifecycle.SHARE_APP_MESSAGE:
            case PageLifecycle.SHARE_TIME_LINE:
                miniOptions[PageLifecycle[key]] = (() => {
                    const lifecycleFunc = createLifecycleMethods(PageLifecycle[key], miniOptions)
                    return function (...args) {
                        const injectLifes = lifecycleFunc.apply(this, args);
                        return injectLifes[injectLifes.length - 1]
                    }
                })()
                break;
            default:
                miniOptions[PageLifecycle[key]] = createLifecycleMethods(PageLifecycle[key], miniOptions)
                break;
        }
    }

    Page(miniOptions)
}